home *** CD-ROM | disk | FTP | other *** search
- /*
- * Copyright 1993, 1994, Silicon Graphics, Inc.
- * All Rights Reserved.
- *
- * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, Inc.;
- * the contents of this file may not be disclosed to third parties, copied or
- * duplicated in any form, in whole or in part, without the prior written
- * permission of Silicon Graphics, Inc.
- *
- * RESTRICTED RIGHTS LEGEND:
- * Use, duplication or disclosure by the Government is subject to restrictions
- * as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data
- * and Computer Software clause at DFARS 252.227-7013, and/or in similar or
- * successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished -
- * rights reserved under the Copyright Laws of the United States.
- */
- /*
- * tabletogl.c : an openGL-Xlib tablet "line-drawing" demo program.
- * This is the "after" version, ported from its 4.0
- * GLX mixed model "before" counterpart, located at
- * ../../GLX/tablet/tabletglx.c
- *
- * tabletogl imitates a pen (stylus) drawing on a surface:
- * while the pen (stylus' button) is pressed down, a line
- * continues to be drawn. when the pen is released, the
- * current line stops.
- *
- * There are a function prior to the infinite loop worth noting:
- *
- * setupdevs() finds, opens, and creates a handle to the "tablet"
- * device structure. it then determines the given type and class
- * of each device event we're going to be interested in, and then
- * makes requests to the server to send us events that match the
- * events and devices described by the event list *and* that come
- * from our specific window.
- *
- * Following this, the get/process input infinite loop occupies the
- * rest of the program's energies. the core of this is the "default"
- * portion of the "switch (event.type)" statement which catches the
- * tablet events being generated. notice this is where the
- * {tablet_motion_type, tablet_press_type, tablet_release_type} vars
- * come into play: recall these were defined in setupdevs() with the
- * 3 macros DeviceMotionNotify, DeviceButtonPress, and
- * DeviceButtonRelease, respectively. XSelectExtensionEvent then was
- * used to ask the server to send us any events generated by these
- * devices in our window.
-
- * ratmandu -- ported to openGL, aug 93
- */
- #include <stdio.h>
- #include <stdlib.h>
- #include <GL/gl.h>
- #include <GL/glx.h>
- #include <GL/glu.h>
- #include <X11/Xlib.h>
- #include <X11/Xutil.h>
- #include <X11/Xos.h>
- #include <X11/Xatom.h>
- #include <X11/extensions/XI.h>
- #include <X11/extensions/XInput.h>
-
- #define YELLOW 3
- #define BLUE 4
- #define CYAN 6
-
- #define X 0
- #define Y 1
- #define MAXRESOLUTION 2206
-
- /* line drawing structures to create a new linked-list everytime the
- * stylus (button) is pressed down and grow that list as long as
- * MotionNotify events occur. when the stylus (button) is released,
- * free up the list and reset the head and tail to point to null
- */
- #define newll() ((struct lineElement *)malloc(sizeof(struct lineElement)))
-
- struct lineElement {
- long xy[2];
- struct lineElement *next;
- };
-
- typedef struct {
- struct lineElement *head, *tail;
- } LineList;
-
- LineList lineLs;
- struct lineElement *lineDummy;
-
- Display *dpy; /* The X server connection */
- Atom del_atom; /* WM_DELETE_WINDOW atom */
- Window glwin; /* handle to the GL window */
-
- static void openwindow(char *);
- static void setupdevs(void);
- static void makeframe(void);
- static void drawcurrentline(void);
- static void clean_exit(void);
- static void makeRasterFont(Display **dpy);
- static void printString(char *s);
-
- int tablet_device_id; /* device handles for the */
- int tablet_press_type; /* the following 3 tablet device ID handles are */
- int tablet_release_type; /* defined in setupdevs() and then used in the */
- int tablet_motion_type; /* infinite (get/process input) loop in main */
-
- float ratio; /* stores the current ratio to scale the tablet's full */
- /* range of coordinates into the window's current size */
- int xsize, ysize; /* stores window's current size values */
- XEvent event;
-
-
-
- void main(int argc, char *argv[])
- {
- int i;
- long xprev, yprev; /* used if either the x or y stylus val doesn't chng */
- int myExpose, myConfigure, myMotion,
- myButtPress, myButtRelease, myButtDown; /* store which events occur */
-
-
-
- myExpose = myConfigure = myMotion = GL_FALSE;
- myButtPress = myButtRelease = myButtDown = GL_FALSE;
-
- openwindow(argv[0]); /* open "window" something like winopen wud do */
- setupdevs(); /* make the necessary connections to the tablet */
- makeRasterFont(&dpy); /* make font */
-
- /*
- * The event loop.
- */
- while (1) { /* standard logic: get event(s), process event(s) */
-
- int axis_data[6];
-
- glFlush(); /* For proper DGL performance */
-
- /* this "do while" loop does the `get events' half of the "get events,
- * process events" action of the infinite while. this is to ensure
- * the event queue is always drained before the events that have come
- * in are processed.
- */
- do {
-
- XNextEvent(dpy, &event);
- switch (event.type) {
-
- /* "Expose" events are sort of like "REDRAW" in gl-speak in
- * terms of when a window becomes visible, or a previously
- * invisible part becomes visible.
- */
- case Expose: /* Exposures */
- myExpose = GL_TRUE;
- break;
-
-
- /* "ConfigNotify" events are like "REDRAW" in terms of changes
- * to a window's size or position.
- */
- case ConfigureNotify: /* Resize GL manually */
- /* save the changed width/height of the parent X window */
- xsize = event.xconfigure.width;
- ysize = event.xconfigure.height;
- ratio = (float) xsize / MAXRESOLUTION;
- myConfigure = GL_TRUE;
- break;
-
- case ButtonRelease: /* Back door exit */
- if (event.xbutton.button == Button1)
- clean_exit();
- break;
-
- case ClientMessage: /* WM invoked exit */
- if (event.xclient.data.l[0] == del_atom)
- clean_exit();
- break;
-
- /* since interest is on the tablet, it becomes the default */
- default:
-
- if ((lineLs.tail != NULL) && /* make sure we've already
- processed a button (stylus)
- press event which sets up
- the linked-list for x/y
- pair storage/line drawing */
-
- (event.type == tablet_motion_type) && /* make sure
- this is a mo-
- tion event */
-
- myButtDown) { /* and make sure button itself is still
- down--myButtPress only processes the
- occurence of the but press (which
- sets up the new linked list) and
- then is immediately reset to FALSE */
-
- /* the body of this if statement processes "Motion" events
- * from any one of the dials. the axes_count element of
- * the XDeviceMotionEvent structure (defined in /usr/include
- * /X11/extensions/XInput.h) is used to determine if there
- * are 2 or only 1 new coordinate value(s):
- * if (axes_count == 2), both X and Y have changed,
- * if (axes_count == 1), only Y has changed--X has not, and
- * if (axes_count == 0), only X has changed--Y has not.
- */
- XDeviceMotionEvent *M = (XDeviceMotionEvent *) &event;
-
- if (M->axes_count != 2) { /* if x OR y didn't chng */
- xprev = lineLs.tail->xy[X];/* axes_count < 2 so */
- yprev = lineLs.tail->xy[Y];/* need to save prev */
- }
- lineDummy = newll(); /* alloc a new element */
- lineDummy->next = NULL; /* for current line */
- lineLs.tail->next = lineDummy; /* point tail to it */
- lineLs.tail = lineDummy;
-
- if (M->axes_count == 2) { /* if new x/y pair, */
-
- lineLs.tail->xy[X] = M->axis_data[X];
- lineLs.tail->xy[Y] = M->axis_data[Y];
-
- } else if (M->first_axis == 0) {/* elseif new X val */
-
- lineLs.tail->xy[X] = M->axis_data[0];
- lineLs.tail->xy[Y] = yprev;
-
- } else if (M->first_axis == 1) {/* elseif new Y val */
-
- lineLs.tail->xy[X] = xprev;
- lineLs.tail->xy[Y] = M->axis_data[0];
-
- }
- myMotion = GL_TRUE;
-
- } else if (event.type == tablet_press_type) {
-
- XDeviceButtonEvent *P = (XDeviceButtonEvent *) &event;
-
- xprev = P->axis_data[0]; /* butt's been pressed so */
- yprev = P->axis_data[1]; /* now start to make a new */
- myButtPress = GL_TRUE; /* line. this cur pnt'll */
- myButtDown = GL_TRUE; /* be the "move to" coord */
-
- } else if (event.type == tablet_release_type) {
-
- myButtRelease = GL_TRUE;
- myButtDown = GL_FALSE;
- }
- break;
-
- } /* end switch (event.type) */
-
- } while (XPending(dpy)); /* end "do { } while".
- * XPending() is like qtest()--it only
- * tells you if there're any events
- * presently in the queue. it does not
- * disturb queue's contents in any way.
- */
-
- /* On an "Expose" event, redraw the affected window
- */
- if (myExpose) {
- makeframe(); /* draw the GL stuff */
- myExpose = GL_FALSE; /* reset flag--queue now empty */
- }
-
- /* On a "ConfigureNotify" event, resize window (XMoveResizeWindow),
- * and then redraw contents.
- */
- if (myConfigure) {
- glViewport(0, 0, xsize-1, ysize-1);
- makeframe();
- myConfigure = GL_FALSE; /* reset flag--queue now empty */
- }
-
- /* a motion-type event (the next x/y pair was already saved up above)
- * means we're still drawing more along the current line.
- */
- if (myMotion) {
- drawcurrentline(); /* butt still pressed, pen still moving, */
- /* keep drawing at end of current line */
- myMotion = GL_FALSE; /* reset flag--queue now empty */
- }
-
- /* a "button press"-type event means we're starting a new line so we
- * need to re-initialize/create our linked-list.
- */
- if (myButtPress) {
- XDeviceButtonEvent *B = (XDeviceButtonEvent *) &event;
-
- lineDummy = newll(); /* making a new line so start a */
- lineDummy->next = NULL; /* new list. point head and tail */
- lineLs.head = lineDummy; /* to it, and assign current new */
- lineLs.tail = lineDummy; /* point to "tail" of list */
- lineLs.tail->xy[X] = xprev; /* make our first point be what */
- lineLs.tail->xy[Y] = yprev; /* we saved up above */
- myButtPress = GL_FALSE; /* reset flag--queue now empty */
- }
-
- /* a "button release"-type event means the current line is complete
- * so now we need to free up the current linked-list.
- */
- if (myButtRelease) {
- struct lineElement *ptr;
- for (ptr = lineLs.head; ptr->next != NULL; ptr = ptr->next) {
- free(ptr); /* "empty" our current list */
- }
- lineLs.head = NULL;
- lineLs.tail = NULL;
- myButtRelease = GL_FALSE; /* reset flag--queue now empty */
- }
- }
- }
-
-
- static int attributeList[] = { None }; /* use the default graphics visual */
-
-
- /* WaitForNotify:
- * used to make sure the MapWindow() calls inside openwindow() occur
- * beFORE glXMakeCurrent() is invoked so as to avoid a race condition.
- */
- static Bool WaitForNotify(Display *d, XEvent *e, char *arg) {
- return (e->type == MapNotify) && (e->xmap.window == (Window)arg);
- }
-
-
- /* openwindow -
- * establish connection to X server, get screen info, specify the
- * attributes we want the WM to try to provide, and create the GL window
- */
- static void openwindow(char *progname) {
-
- int scrnnum; /* X screen number */
- int xorig, yorig; /* window (upper-left) origin */
- long scrnheight;
- XSizeHints Winhints; /* used to fix window size */
- Colormap cmap;
- GLXContext cx;
- XVisualInfo *vi;
- XSetWindowAttributes swa;
- XColor colorstruct;
-
-
- /* Connect to the X server and get screen info */
- if ((dpy = XOpenDisplay(NULL)) == NULL) {
- fprintf(stderr, "%s: cannot connect to X server %s\n",
- progname, XDisplayName(NULL));
- exit(1);
- }
- scrnnum = DefaultScreen(dpy);
- scrnheight = DisplayHeight(dpy, scrnnum);
-
- /* define window (upper-left) origin coords */
- xorig = 0;
- yorig = 0;
- xsize = 500;
- ysize = 500;
-
- ratio = (float) xsize / MAXRESOLUTION; /* calculate ratio to scale
- full tablet into window */
-
- /* get an appropriate visual */
- vi = glXChooseVisual(dpy, DefaultScreen(dpy), attributeList);
- if (vi == NULL) {
- printf("Couldn't get default visual (???)\n");
- exit(0);
- }
-
- /* create a GLX context */
- cx = glXCreateContext(dpy, vi, NULL, GL_TRUE);
- if (cx == NULL) {
- printf("Couldn't get context.\n");
- exit(0);
- }
-
- /* create a colormap */
- cmap = XCreateColormap(dpy, RootWindow(dpy, vi->screen),
- vi->visual, AllocAll);
- /* create a window */
- swa.colormap = cmap;
- swa.border_pixel = 0;
-
- /* express interest in certain events */
- swa.event_mask = StructureNotifyMask | ButtonPressMask |
- ButtonReleaseMask | ExposureMask;
- glwin = XCreateWindow(dpy, RootWindow(dpy, vi->screen),
- xorig, yorig, xsize, ysize,
- 0, vi->depth, InputOutput, vi->visual,
- CWBorderPixel|CWColormap|CWEventMask, &swa);
-
- XMapWindow(dpy, glwin);
- XIfEvent(dpy, &event, WaitForNotify, (char*)glwin);
-
- XSync(dpy, GL_FALSE);
- /* connect the context to the window */
- if (!glXMakeCurrent(dpy, glwin, cx) == GL_TRUE) {
- fprintf(stderr, "error w/glXMakeCurrent: cudn't set");
- fprintf(stderr, " context to the singlebuffered GL window\n");
- exit(-1);
- }
-
- /* specify the values for the Window Size Hints we want to enforce: this
- * window's aspect ratio needs to stay at 1:1, constrain min and max
- * window size, and specify the initial origin and size of the window.
- */
- Winhints.x = xorig; /* specify desired upper-left corner origin */
- Winhints.y = yorig; /* of window so prog will place itself */
- Winhints.width = xsize; /* specify desired x/y size of window */
- Winhints.height = ysize;
- Winhints.min_width = xsize/4; /* define min and max */
- Winhints.max_width = scrnheight-1; /* width and height */
- Winhints.min_height = ysize/4;
- Winhints.max_height = scrnheight-1;
- Winhints.min_aspect.x = 1; /* keep aspect at a 1:1 ratio */
- Winhints.max_aspect.x = 1;
- Winhints.min_aspect.y = 1;
- Winhints.max_aspect.y = 1;
- /* set the corresponding flags */
- Winhints.flags = USPosition|USSize|PMaxSize|PMinSize|PAspect;
- XSetNormalHints(dpy, glwin, &Winhints);
-
- /* define string that will show up in the window title bar (and icon) */
- XStoreName(dpy, glwin, "opengl tablet \"line drawing\" program");
-
- /* declare interest in events we want the window to process */
- XSelectInput(dpy, glwin, StructureNotifyMask | ExposureMask |
- ButtonPressMask | ButtonReleaseMask);
-
- XSetWMColormapWindows(dpy, glwin, &glwin, 1);
-
- /* express interest in WM killing this app */
- if ((del_atom = XInternAtom(dpy, "WM_DELETE_WINDOW", GL_TRUE)) != None)
- XSetWMProtocols(dpy, glwin, &del_atom, 1);
-
- colorstruct.pixel = YELLOW;
- colorstruct.red = 65535;
- colorstruct.green = 65535;
- colorstruct.blue = 0;
- colorstruct.flags = DoRed | DoGreen | DoBlue;
- XStoreColor(dpy, cmap, &colorstruct);
- colorstruct.pixel = BLUE;
- colorstruct.red = 0;
- colorstruct.green = 0;
- colorstruct.blue = 35535;
- colorstruct.flags = DoRed | DoGreen | DoBlue;
- XStoreColor(dpy, cmap, &colorstruct);
- colorstruct.pixel = CYAN;
- colorstruct.red = 0;
- colorstruct.green = 65535;
- colorstruct.blue = 65535;
- colorstruct.flags = DoRed | DoGreen | DoBlue;
- XStoreColor(dpy, cmap, &colorstruct);
-
- glLineWidth(3.0);
- glFlush();
- }
-
-
- /* setupdevs -
- *
- * establish a live connection to the tablet device.
- *
- * leverages off the "X11 Input Extension Library Specification"
- * document (you *shud* be able to locate the on-line public access
- * directory which contains all the files to print hard-copy of this
- * document under .../mit/doc/extensions/xinput). refer to
- * /usr/include/X11/extensions/{XI.h, XInput.h} for structures accessed.
- */
- static void setupdevs() {
-
- int i, ndevices;
- XDevice *tablet_device;
- XDeviceInfoPtr lp, list;
- int num_ext_event_classes;
- XEventClass ListOfEventClass[3];
- int tablet_press_class, tablet_release_class, tablet_motion_class;
-
-
-
- /* get a ptr to the list of all currently defined input devices */
- list = (XDeviceInfoPtr) XListInputDevices(dpy, &ndevices);
- if (!list) {
- fprintf(stderr,"XlistInputDevices failed to generate a devices list\n"); exit(1);
- }
-
- /* check out the /usr/people/4Dgifts/examples/devices/input/X/Xlist.c
- * program (which gets compiled into "xlist"). running it will list
- * all the currently available input devices on the machine xlist is
- * run on. there is a LOT that should be studied in the "input" subtree.
- */
- for (lp=list, i=0; i<ndevices; lp++, i++) {
- if (lp->use == IsXExtensionDevice && strcmp(lp->name,"tablet") == 0) {
- break; /* found the right one--now save the ptr (lp) to it */
- }
- }
- if (i == ndevices) {
- fprintf(stderr, "\"tablet\" device not found\n");
- exit(1);
- }
- tablet_device = XOpenDevice(dpy, lp->id); /* open the Tablet device */
- if (!tablet_device) {
- fprintf(stderr, "XOpenDevice failedfor \"tablet\" device\n");
- exit(1);
- }
- tablet_device_id = tablet_device->device_id;
-
- /* the following 3 macros determine the given event's type and class.
- * each macro is passed the structure that describes the device from
- * which input is desired.
- */
- DeviceButtonPress(tablet_device, tablet_press_type, tablet_press_class);
- DeviceButtonRelease(tablet_device, tablet_release_type, tablet_release_class);
- DeviceMotionNotify(tablet_device, tablet_motion_type, tablet_motion_class);
-
- ListOfEventClass[0]=tablet_press_class;
- ListOfEventClass[1]=tablet_release_class;
- ListOfEventClass[2]=tablet_motion_class;
- num_ext_event_classes = 3;
-
- /* XSelectExtensionEvent requests the server to send events that match
- * the events and devices described by the event list and that come
- * from the requested window.
- */
- XSelectExtensionEvent(dpy, glwin, ListOfEventClass, num_ext_event_classes);
-
- }
-
-
-
- /* draw the tablet's current line segment now that the event queue is drained
- */
- static void drawcurrentline(void)
- {
- struct lineElement *ptr;
- #ifdef GIVES_BROKEN_LINE__NOT_SURE_WHY
- long vect[2];
-
- glIndexi(CYAN);
-
- glBegin(GL_LINES); /* draw our current line segment */
-
- for (ptr = lineLs.head; ptr->next != NULL; ptr = ptr->next) {
- vect[0] = (long) (ptr->xy[0]*ratio);
- vect[1] = (long) (ptr->xy[1]*ratio);
- glVertex2i(vect[0], vect[1]);
- }
-
- glEnd();
- #endif
- int vect1[2];
- int vect2[2];
-
-
- glIndexi(CYAN);
-
- for (vect1[0] = (int) (lineLs.head->xy[0]*ratio), /* initially get the */
- vect1[1] = (int) (lineLs.head->xy[1]*ratio), /* first vertex */
- ptr = lineLs.head->next; ptr->next != NULL; ptr = ptr->next) {
-
- vect2[0] = (int) (ptr->xy[0]*ratio); /* get the next vertex */
- vect2[1] = (int) (ptr->xy[1]*ratio);
-
- glBegin(GL_LINES); /* draw our current line segment */
- glVertex2i(vect1[0], vect1[1]);
- glVertex2i(vect2[0], vect2[1]);
- glEnd();
-
- vect1[0] = vect2[0]; /* now save the last vertex into the */
- vect1[1] = vect2[1]; /* beginning of the next line segment */
- }
- }
-
-
-
- /* makeframe -- Draw the tablet "background" in the GL window
- */
- static void makeframe()
- {
- glClearIndex((GLfloat) BLUE);
- glClear(GL_COLOR_BUFFER_BIT);
- glLoadIdentity();
- gluOrtho2D(-0.5, xsize-0.5, -0.5, ysize-0.5);
-
- glIndexi(YELLOW);
- glRasterPos2i(4, 5);
- printString("Use left mouse button to quit.");
-
- glFlush();
- }
-
-
- /* makeRasterFont() and printString() are lifted out of font.c (lives in
- * this same directory) as a replacement to the IrisGL charstr() function.
- */
- GLuint base;
-
- static void makeRasterFont(Display **dpy)
- {
- XFontStruct *fontInfo;
- Font id;
- unsigned int first, last;
-
- fontInfo = XLoadQueryFont(*dpy,
- "-sgi-screen-bold-r-normal--15-150-72-72-m-90-iso8859-1");
- if (fontInfo == NULL) {
- printf("no font found\n");
- exit(0);
- }
- id = fontInfo->fid;
- first = fontInfo->min_char_or_byte2;
- last = fontInfo->max_char_or_byte2;
- base = glGenLists(last+1);
- if (base == 0) {
- printf("out of display lists\n");
- exit(0);
- }
- glXUseXFont(id, first, last-first+1, base+first);
- }
-
- static void printString(char *s)
- {
- glPushAttrib(GL_LIST_BIT);
- glListBase(base);
- glCallLists(strlen(s), GL_UNSIGNED_BYTE, (unsigned char *)s);
- glPopAttrib();
- }
-
-
-
- /* clean_exit -- Clean up before exiting
- */
- static void clean_exit(void)
- {
- XCloseDisplay(dpy);
- exit(0);
- }
-